/* V810 Emulator
 *
 * Copyright (C) 2006 David Tucker
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

//////////////////////////////////////////////////////////
// CPU Debug routines

//what do we realy need?
//#include <stdio.h>
//#include <stdlib.h> 
//#include <string.h>
//#include <ctype.h>

#include <mednafen/mednafen.h>
#include "v810_opt.h"
#include "v810_cpu.h"
#include "v810_cpuD.h"


////////////////////////////////////////////////////////////
// Defines

//Structure to store an element in our linked list
// used to dynamicaly dissasemble a rom
typedef struct dasms {
	int            offset;
	uint32 		   PC;
	uint32           jump;
      struct dasms * nextElement;
} dasmS;

typedef struct {
   int addr_mode;               // Addressing mode
   const char * opname;         // Opcode name (string)
} operation;

static const operation optable[80] = {
  { AM_I,       "mov  " },           // 0x00
  { AM_I,       "add  " },           // 0x01
  { AM_I,       "sub  " },           // 0x02
  { AM_I,       "cmp  " },           // 0x03
  { AM_I,       "shl  " },           // 0x04
  { AM_I,       "shr  " },           // 0x05
  { AM_I,       "jmp  " },           // 0x06
  { AM_I,       "sar  " },           // 0x07
  { AM_I,       "mul  " },           // 0x08
  { AM_I,       "div  " },           // 0x09
  { AM_I,       "mulu " },           // 0x0A
  { AM_I,       "divu " },           // 0x0B
  { AM_I,       "or   " },           // 0x0C
  { AM_I,       "and  " },           // 0x0D
  { AM_I,       "xor  " },           // 0x0E
  { AM_I,       "not  " },           // 0x0F

  { AM_II,      "mov  " },           // 0x10  // Imediate
  { AM_II,      "add  " },           // 0x11
  { AM_II,      "setf " },           // 0x12
  { AM_II,      "cmp  " },           // 0x13
  { AM_II,      "shl  " },           // 0x14
  { AM_II,      "shr  " },           // 0x15
  {0x8000|AM_IX,"ei   " },	     // 0x16  // EI(VB only)
  { AM_II,      "sar  " },           // 0x17
  { AM_II,      "trap " },           // 0x18

  { AM_IX,      "reti " },           // 0x19  //BRKRETI
  { AM_IX,      "halt " },           // 0x1A  //STBY

  {AM_UDEF,     "???  " },           // 0x1B  // Unknown
  { AM_II,      "ldsr " },           // 0x1C
  { AM_II,      "stsr " },           // 0x1D
  {0x8000|AM_IX,"di   " },           // 0x1E  // DI(VB only)
  {AM_BSTR,     "BSTR " },           // 0x1F  // Special Bit String Instructions

  {AM_UDEF,     "???  " },           // 0x20  // Unknown   // This is a fudg on our part
  {AM_UDEF,     "???  " },           // 0x21  // Unknown   // We have 6 and 7 bit instructions
  {AM_UDEF,     "???  " },           // 0x22  // Unknown   // this is filld in by the Conditional Branch Instructions
  {AM_UDEF,     "???  " },           // 0x23  // Unknown
  {AM_UDEF,     "???  " },           // 0x24  // Unknown
  {AM_UDEF,     "???  " },           // 0x25  // Unknown
  {AM_UDEF,     "???  " },           // 0x26  // Unknown
  {AM_UDEF,     "???  " },           // 0x27  // Unknown

  { AM_V,       "movea" },           // 0x28
  { AM_V,       "addi " },           // 0x29
  { AM_IV,      "jr   " },           // 0x2A
  { AM_IV,      "jal  " },           // 0x2B
  { AM_V,       "ori  " },           // 0x2C
  { AM_V,       "andi " },           // 0x2D
  { AM_V,       "xori " },           // 0x2E
  { AM_V,       "movhi" },           // 0x2F

  { AM_VIa,     "ld.b " },           // 0x30
  { AM_VIa,     "ld.h " },           // 0x31
  {AM_UDEF,     "???  " },           // 0x32  // Unknown
  { AM_VIa,     "ld.w " },           // 0x33
  { AM_VIb,     "st.b " },           // 0x34
  { AM_VIb,     "st.h " },           // 0x35
  {AM_UDEF,     "???  " },           // 0x36  // Unknown
  { AM_VIb,     "st.w " },           // 0x37
  { AM_VIa,     "in.b " },           // 0x38
  { AM_VIa,     "in.h " },           // 0x39
  { AM_VIa,     "caxi " },           // 0x3A
  { AM_VIa,     "in.w " },           // 0x3B
  { AM_VIb,     "out.b" },           // 0x3C
  { AM_VIb,     "out.h" },           // 0x3D
  { AM_FPP,     "FPP  " },           // 0x3E  //Floating Point Instruction, Special Case
  { AM_VIb,     "out.w" },           // 0x3F

  { AM_III,     "bv   " },           // 0x40
  { AM_III,     "bl   " },           // 0x41  //BC  0x41
  { AM_III,     "be   " },           // 0x42  //BZ  0x42
  { AM_III,     "bnh  " },           // 0x43
  { AM_III,     "bn   " },           // 0x44
  { AM_III,     "br   " },           // 0x45
  { AM_III,     "blt  " },           // 0x46
  { AM_III,     "ble  " },           // 0x47
  { AM_III,     "bnv  " },           // 0x48
  { AM_III,     "bnl  " },           // 0x49 //BNC 0x49
  { AM_III,     "bne  " },           // 0x4A //BNZ 0x4A
  { AM_III,     "bh   " },           // 0x4B
  { AM_III,     "bp   " },           // 0x4C
  { AM_III,     "nop  " },           // 0x4D
  { AM_III,     "bge  " },           // 0x4E
  { AM_III,     "bgt  " }            // 0x4F
};
// All instructions greater than 0x50 are undefined (this should not be posible of cource)


//Structure for holding the SubOpcodes, Same as above, without the InsType.
typedef struct {
   const char * opname;               // Opcode name (string)
} suboperation;


//  Bit String Subopcodes
static const suboperation bssuboptable[16] = {
  { "SCH0BSU" },           // 0x00
  { "SCH0BSD" },           // 0x01
  { "SCH1BSU" },           // 0x02
  { "SCH1BSD" },           // 0x03
  { "BError4" },           // 0x04  // Unknown
  { "BError5" },           // 0x05  // Unknown
  { "BError6" },           // 0x06  // Unknown
  { "BError7" },           // 0x07  // Unknown
  { "ORBSU  " },           // 0x08
  { "ANDBSU " },           // 0x09
  { "XORBSU " },           // 0x0A
  { "MOVBSU " },           // 0x0B
  { "ORNBSU " },           // 0x0C
  { "ANDNBSU" },           // 0x0D
  { "XORNBSU" },           // 0x0E
  { "NOTBSU " }            // 0x0F
};

//  Floating Point Subopcodes
static const suboperation fpsuboptable[16] = {
  { "cmpf.s " },           // 0x00
  { "FError1" },           // 0x01  // Unknown
  { "cvt.ws " },           // 0x02
  { "cvt.sw " },           // 0x03
  { "addf.s " },           // 0x04
  { "subf.s " },           // 0x05
  { "mulf.s " },           // 0x06
  { "divf.s " },           // 0x07

  { "FError8" },           // 0x08  // Invalid
  { "FError9" },           // 0x09  // Invalid
  { "FErrorA" },           // 0x0A  // Invalid
  { "FErrorB" },           // 0x0B  // Invalid
  { "FErrorC" },           // 0x0C  // Invalid
  { "FErrorD" },           // 0x0D  // Invalid
  { "FErrorE" },           // 0x0E  // Invalid
  { "FErrorF" }            // 0x0F  // Invalid
};

static const suboperation fpsuboptable_vb[16] = {
  { "cmpf.s " },           // 0x00
  { "FError1" },           // 0x01  // Unknown
  { "cvt.ws " },           // 0x02
  { "cvt.sw " },           // 0x03
  { "addf.s " },           // 0x04
  { "subf.s " },           // 0x05
  { "mulf.s " },           // 0x06
  { "divf.s " },           // 0x07
  { "XB" },                // 0x08  // undocumented
  { "XH" },                // 0x09  // undocumented //VFishing???
  { "REV" },               // 0x0A  // undocumented
  { "trnc.sw" },           // 0x0B
  { "MPYHW" },             // 0x0C  // undocumented
  { "FErrorD" },           // 0x0D  // Unknown
  { "FErrorE" },           // 0x0E  // Unknown
  { "FErrorF" }            // 0x0F  // Unknown
};

static const char *pretty_preg_names[32] =
{
 "r0", "r1", "hsp", "sp", "gp", "tp", "r6", "r7",
 "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
 "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
 "r24", "r25", "r26", "r27", "r28", "r29", "r30", "lp",
};

static const char *pretty_sreg_names[32] =
{
 "sr0(eipc)", "sr1(eipsw)", "sr2(fepc)", "sr3(fepsw)", "sr4(ecr)", "sr5(psw)", "sr6(pir)", "sr7(tkcw)",
 "sr8(invalid)", "sr9(invalid)", "sr10(invalid)", "sr11(invalid)", "sr12(invalid)", "sr13(invalid)", 
 "sr14(invalid)", "sr15(invalid)", "sr16(invalid)", "sr17(invalid)", "sr18(invalid)", "sr19(invalid)", 
 "sr20(invalid)", "sr21(invalid)", "sr22(invalid)", "sr23(invalid)", 
 "sr24(chcw)", "sr25(adtre)", "sr26(invalid)", "sr27(invalid)", "sr28(invalid)", "sr29(invalid)", "sr30(invalid)", 
 "sr31(invalid)" 
};

void v810_dis(uint32 &tPC, int num, char *buf, uint16 (*rhword)(uint32), bool vbmode)
{
    int lowB, highB, lowB2, highB2;             // up to 4 bytes for instruction (either 16 or 32 bits)
    int opcode, arg1, arg2, arg3;
    int i = 0;

    buf[0] = 0;

    for(i = 0; i< num; i++) 
    {
     const uint16 hw0 = rhword(tPC);
     const uint16 hw1 = rhword(tPC + 2);

     lowB = hw0 & 0xFF;
     highB = hw0 >> 8;

     lowB2 = hw1 & 0xFF;
     highB2 = hw1 >> 8;

        opcode = highB >> 2;
        if((highB & 0xE0) == 0x80)        // Special opcode format for
            opcode = (highB >> 1);            // type III instructions.

        if((opcode > 0x4F) | (opcode < 0)) {
            //Error Invalid opcode!
            sprintf(&buf[strlen(buf)], "0x%04x", hw0);
            tPC += 2;
        }

	int am = optable[opcode].addr_mode;

	if((am & 0x8000) && !vbmode)
	 am = AM_UDEF;

	am &= ~0x8000;

        switch(am) {
        case AM_I:       // Do the same Ither way =)
            arg1 = (lowB >> 5) + ((highB & 0x3) << 3);
            arg2 = (lowB & 0x1F);
            if (opcode == JMP) {
                sprintf(&buf[strlen(buf)],"%s    [%s]", optable[opcode].opname, pretty_preg_names[arg2]);
            } else {
                sprintf(&buf[strlen(buf)],"%s    %s, %s", optable[opcode].opname, pretty_preg_names[arg2], pretty_preg_names[arg1]);
            }
            tPC += 2;   // 16 bit instruction
            break;
        case AM_II:
            arg1 = (lowB >> 5) + ((highB & 0x3) << 3);
            arg2 = (lowB & 0x1F);
			if(opcode == LDSR) {
				sprintf(&buf[strlen(buf)],"%s    %s, %s", optable[opcode].opname, pretty_preg_names[arg1], pretty_sreg_names[arg2]);
			} else if(opcode == STSR) {
				sprintf(&buf[strlen(buf)],"%s     %s, %s", optable[opcode].opname, pretty_sreg_names[arg2], pretty_preg_names[arg1]);
			} else if(opcode == ADD_I || opcode == CMP_I || opcode == MOV_I) {
				sprintf(&buf[strlen(buf)],"%s    %d, %s", optable[opcode].opname, sign_5(arg2), pretty_preg_names[arg1]);
			} else {
                                sprintf(&buf[strlen(buf)],"%s    %d, %s", optable[opcode].opname, arg2, pretty_preg_names[arg1]);
			}
            tPC += 2;   // 16 bit instruction
            break;
        case AM_III:
            arg1 = ((highB & 0x1) << 8) + (lowB & 0xFE);
	    if(opcode == NOP)
             sprintf(&buf[strlen(buf)],"%s", optable[opcode].opname);
	    else
             sprintf(&buf[strlen(buf)],"%s    %08x", optable[opcode].opname, tPC + sign_9(arg1));
            tPC += 2;   // 16 bit instruction
            break;
        case AM_IV:
            arg1 = ((highB & 0x3) << 24) + (lowB << 16) + (highB2 << 8) + lowB2;

            sprintf(&buf[strlen(buf)],"%s    %08x", optable[opcode].opname, tPC + sign_26(arg1));

            tPC += 4;                                               // 32 bit instruction
            break;
        case AM_V:       
            arg1 = (lowB >> 5) + ((highB & 0x3) << 3);
            arg2 = (lowB & 0x1F);
            arg3 = (highB2 << 8) + lowB2;

	    // TODO: What would be the best way to disassemble the MOVEA instruction?
	    //if(opcode == MOVEA)
            // sprintf(&buf[strlen(buf)],"%s    0x%X, %s, %s", optable[opcode].opname, (uint32)(int32)(int16)(uint16)arg3, pretty_preg_names[arg2], pretty_preg_names[arg1] );
	    //else
            sprintf(&buf[strlen(buf)],"%s    0x%X, %s, %s", optable[opcode].opname, arg3, pretty_preg_names[arg2], pretty_preg_names[arg1] );
            tPC += 4;   // 32 bit instruction
            break;
        case AM_VIa:  // Mode6 form1
            arg1 = (lowB >> 5) + ((highB & 0x3) << 3);
            arg2 = (lowB & 0x1F);
            arg3 = (highB2 << 8) + lowB2;

	    if(!arg3) // Don't bother printing offset if it's 0
             sprintf(&buf[strlen(buf)],"%s    [%s], %s", optable[opcode].opname, pretty_preg_names[arg2], pretty_preg_names[arg1]);
	    else if(sign_16(arg3) >= 0) // Make disassembly prettier if it's a positive offset
             sprintf(&buf[strlen(buf)],"%s    0x%04x[%s], %s", optable[opcode].opname, sign_16(arg3), pretty_preg_names[arg2], pretty_preg_names[arg1]);
	    else
             sprintf(&buf[strlen(buf)],"%s    %d[%s], %s", optable[opcode].opname, sign_16(arg3), pretty_preg_names[arg2], pretty_preg_names[arg1]);

            tPC += 4;   // 32 bit instruction
            break;
        case AM_VIb:  // Mode6 form2
            arg1 = (lowB >> 5) + ((highB & 0x3) << 3);
            arg2 = (lowB & 0x1F);
            arg3 = (highB2 << 8) + lowB2;                              //  whats the order??? 2,3,1 or 1,3,2

	    if(!arg3) // Don't bother printing offset if it's 0
             sprintf(&buf[strlen(buf)],"%s    %s, [%s]", optable[opcode].opname, pretty_preg_names[arg1], pretty_preg_names[arg2]);
            else if(sign_16(arg3) >= 0) // Make disassembly prettier if it's a positive offset
             sprintf(&buf[strlen(buf)],"%s    %s, 0x%04x[%s]", optable[opcode].opname, pretty_preg_names[arg1], sign_16(arg3), pretty_preg_names[arg2]);
	    else
             sprintf(&buf[strlen(buf)],"%s    %s, %d[%s]", optable[opcode].opname, pretty_preg_names[arg1], sign_16(arg3), pretty_preg_names[arg2]);
            tPC += 4;   // 32 bit instruction
            break;
        case AM_VII:   // Unhandled
            sprintf(&buf[strlen(buf)],"0x%2x 0x%2x 0x%2x 0x%2x", lowB, highB, lowB2, highB2);
            tPC +=4;        // 32 bit instruction
            break;
        case AM_VIII:  // Unhandled
            sprintf(&buf[strlen(buf)],"0x%2x 0x%2x 0x%2x 0x%2x", lowB, highB, lowB2, highB2);
            tPC += 4;   // 32 bit instruction
            break;
        case AM_IX:
            arg1 = (lowB & 0x1); // Mode ID, Ignore for now
            sprintf(&buf[strlen(buf)],"%s", optable[opcode].opname);   
            tPC += 2;   // 16 bit instruction
            break;
        case AM_BSTR:  // Bit String Subopcodes
            arg1 = (lowB >> 5) + ((highB & 0x3) << 3);
            arg2 = (lowB & 0x1F);
            if(arg2 > 15) {
                sprintf(&buf[strlen(buf)],"BError");
            } else {
                //sprintf(&buf[strlen(buf)],"%s, $%d", bssuboptable[arg2].opname,arg1);
		sprintf(&buf[strlen(buf)], "%s", bssuboptable[arg2].opname);
            }
            tPC += 2;   // 16 bit instruction
            break;
        case AM_FPP:   // Floating Point Subcode
            arg1 = (lowB >> 5) + ((highB & 0x3) << 3);
            arg2 = (lowB & 0x1F);
            arg3 = (highB2 >> 2);

            if(arg3 > 15) {
                sprintf(&buf[strlen(buf)],"(Invalid FPU: 0x%02x)", arg3);
            } else {
                sprintf(&buf[strlen(buf)],"%s  %s, %s", (vbmode ? fpsuboptable_vb[arg3].opname : fpsuboptable[arg3].opname), pretty_preg_names[arg2], pretty_preg_names[arg1]);
            }
            tPC += 4;   // 32 bit instruction
            break;
        case AM_UDEF:  // Invalid opcode.
        default:       // Invalid opcode.
            sprintf(&buf[strlen(buf)],"0x%04x", hw0);
            tPC += 2;
        }
    }
}
